package com.heakey.common.component;

import cn.hutool.core.util.ObjectUtil;
import com.heakey.common.exception.BusinessException;
import com.heakey.common.model.XlsCommonTips;
import com.heakey.common.util.ExcelTools;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * 通用XLS解析文件（带错误提示）
 * @param <I>
 */
public class ParseXmlComponent<I extends XlsCommonTips>{
    // 文件
    private MultipartFile file;
    // 解析后的数据
    private List<I> inputData;
    // 数据类型
    private Class<I> iClass;
    // 列数据集合
    private Map<?,?> colMap;

    // 是否有错误提示
    private Boolean hasTips = Boolean.FALSE;

    private ParseXmlComponent(MultipartFile file, Class<I> iClass){
        this.file = file;
        this.iClass = iClass;
    }

    /**
     * 构建
     * @param file
     * @return
     */
    public static <I extends XlsCommonTips> ParseXmlComponent<I> of(MultipartFile file, Class<I> iClass){
        return new ParseXmlComponent(file,iClass);
    }

    /**
     * 通用解析模版方法
     * @param consumer
     * @return
     * @throws Exception
     */
    public ParseXmlComponent<I> commonParse(Consumer<I> consumer) throws Exception {
        long maxSizeBytes = 1024*1024;
        int maxRows = 1000;
        this.parse(maxSizeBytes,maxRows,consumer);
        return this;
    }

    /**
     * 通用解析模版方法
     * @param consumer
     * @return
     * @throws Exception
     */
    private ParseXmlComponent<I> parse(long maxSizeBytes,int maxRows,Consumer<I> consumer) throws Exception {
        // 校验文件大小
        this.maxFileSize(maxSizeBytes);
        // 初始化数据
        this.initData();
        // 校验数据行数
        this.maxRows(maxRows);
        // 校验数据
        this.check(consumer);
        return this;
    }

    /**
     * 获取原始数据
     * @return
     */
    public List<I> getInputData(){
        checkInit();
        return inputData;
    }

    /**
     * 获取是否有错误提示
     * @return
     */
    public Boolean hasTips() {
        checkInit();
        return hasTips;
    }

    /**
     * 收集属性
     * @param funcs
     * @return
     * @param <R>
     */
    public <R> ParseXmlComponent<I> mapCol(final Function<I,R> ...funcs){
        return mapColList(funcs);
    }

    /**
     * 获取某个属性集合
     * @param func
     * @return
     * @param <R>
     */
    public <R> List<R> getColList(final Function<I,R> func){
        return (List<R>)colMap.get(func);
    }

    /**
     * 收集属性
     * @param funcs
     * @return
     * @param <R>
     */
    private <R> ParseXmlComponent<I> mapColList(final Function<I,R> ...funcs){
        checkInit();
        Map<Function<I,R>,List<R>> resultMap = new HashMap<>(funcs.length);
        for(Function<I,R> func : funcs){
            List<R> colList = new ArrayList<>(inputData.size());
            for(I i : inputData) {
                colList.add(func.apply(i));
            }
            resultMap.put(func,colList);
        }
        this.colMap = resultMap;
        return this;
    }

    /**
     * 检查文件大小
     * @param maxSizeBytes
     * @return
     */
    private ParseXmlComponent<I> maxFileSize(long maxSizeBytes){
        long fileSize = file.getSize();
        if(fileSize > maxSizeBytes){
            BusinessException.throwBusException("导入文件大小不能超过"+maxSizeBytes/(1024*1024)+"M");
        }
        return this;
    }

    /**
     * 解析文件
     * @throws Exception
     */
    private ParseXmlComponent<I> initData() throws Exception {
        inputData = ExcelTools.importExcel(iClass,file.getInputStream());
        return this;
    }

    /**
     * 检查数据行数
     * @param maxRows
     * @return
     * @param
     */
    private ParseXmlComponent<I> maxRows(int maxRows){
        checkInit();
        if (inputData.size() > maxRows) {
            BusinessException.throwBusException("导入文件的行数不能超过" + maxRows);
        }
        return this;
    }

    /**
     * 校验数据
     * @param
     */
    private ParseXmlComponent<I> check(Consumer<I> consumer){
        checkInit();
        for(I i : inputData){
            consumer.accept(i);
            // 有错误提示
            if(!hasTips && i.getHasTips()){
                hasTips = true;
            }
        }
        return this;
    }

    /**
     * 检查是否初始化
     */
    private void checkInit(){
        if(ObjectUtil.isNull(inputData)){
            BusinessException.throwBusException("待初始化");
        }
    }
}
